/**
 * Copyright 2013 opencxa.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.cxa.rpc.transportChannel;

import java.util.ArrayList;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.cxa.misc.ExceptionUtils;
import org.cxa.rpc.messages.RpcMessage;


/**
 * @author Christopher Armenio
 */
public abstract class TransportChannel
{	
	public enum DisconnectReason
	{
		LOCAL_REQUESTED("locally requested a disconnect"),
		REMOTE_REQUESTED("remotely requested a disconnect"),
		ERROR_SENDING_DATA("failed to send data to remote"),
		KEEPALIVE_FAILED("remote host has become unresponsive"),
		UNKNOWN("unknown reason");
		
		private final String message;
		private DisconnectReason(String msgIn){ this.message = msgIn; }
		
		@Override
		public String toString(){ return this.message; }
	}
	
	
	public interface TransportChannelListener
	{
		public void onConnect();
		public void onDisconnect(DisconnectReason reasonIn);
		public void onMessageReceived(RpcMessage msgIn);
	}
	
	
	public abstract static class TransportChannelListenerAdapter implements TransportChannelListener
	{
		public void onConnect() {}
		public void onDisconnect(DisconnectReason reasonIn) {}
		public void onMessageReceived(RpcMessage msgIn) {}
	}
	
	
	protected final Logger logger;
	protected final List<TransportChannelListener> listeners = new ArrayList<TransportChannelListener>();
	protected DisconnectReason userSpecifiedDisconnectReason = null;
	
	
	/**
	 * Initializes the TransportChannel, loggers, etc
	 */
	protected TransportChannel()
	{
		this.logger = LogManager.getLogger(this.toString());
	}
	
	
	public void addTransportChannelListener(TransportChannelListener listenerIn)
	{
		if( listenerIn == null ) throw new IllegalArgumentException("listener must NOT be null");
		this.listeners.add(listenerIn);
	}
	
	
	public void disconnect(DisconnectReason reasonIn)
	{
		this.userSpecifiedDisconnectReason = reasonIn;
		this.disconnect();
	}
	
	
	protected void notifyListeners_onConnect()
	{
		for( TransportChannelListener currListener : this.listeners )
		{
			try{ currListener.onConnect(); }
			catch( Exception e ) { this.logger.warn(ExceptionUtils.getExceptionLogString(e, ExceptionUtils.MSG_OPERATION_WILL_CONTINUE)); }
		}
	}
	
	
	protected void notifyListeners_onDisconnect(DisconnectReason reasonIn)
	{
		for( TransportChannelListener currListener : this.listeners )
		{
			try { currListener.onDisconnect((this.userSpecifiedDisconnectReason == null) ? reasonIn : this.userSpecifiedDisconnectReason); }
			catch( Exception e ) { this.logger.warn(ExceptionUtils.getExceptionLogString(e, ExceptionUtils.MSG_OPERATION_WILL_CONTINUE)); }
		}
		this.userSpecifiedDisconnectReason = null;
	}
	
	
	protected void notifyListeners_onMessageReceived(RpcMessage msgIn)
	{
		for( TransportChannelListener currListener : this.listeners )
		{
			try { currListener.onMessageReceived(msgIn); }
			catch( Exception e ) { this.logger.warn(ExceptionUtils.getExceptionLogString(e, ExceptionUtils.MSG_OPERATION_WILL_CONTINUE)); }
		}
	}

	
	
	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString()
	{
		return String.format("%s.%s", this.getClass().getSimpleName(), System.identityHashCode(this));
	}
	
	
	public abstract void disconnect();
	public abstract boolean isConnected();
	public abstract boolean sendMessage(RpcMessage msgIn);
}
